#ifndef __M_SINK_H__
#define __M_SINK_H__
#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include <memory>
#include <mutex>

namespace cl{

/**
 * @brief LogSink 类是日志输出的基类。
 * 
 * 该类提供一个纯虚函数 `log`，子类需要实现具体的日志输出逻辑。
 */
class LogSink{
public:
    using ptr = std::shared_ptr<LogSink>;
    LogSink(){}
    virtual ~LogSink(){}
    // 纯虚函数，子类需要实现具体的日志输出逻辑
    virtual void log(const char* data, size_t len) = 0;
};

/**
 * @brief StdoutSink 类实现了将日志输出到控制台。
 * 
 * 重写了 `log` 方法，通过 `std::cout.write` 将日志数据输出到控制台。
 */
class StdoutSink : public LogSink {
public:
    using ptr = std::shared_ptr<StdoutSink>;
    StdoutSink() = default;
    void log(const char* data, size_t len) override {
        std::cout.write(data, len);
        std::cout.flush();  // 确保日志立即输出*
    }
};

/**
 * @brief FileSink 类实现了将日志输出到指定文件。
 * 
 * 构造时接受文件名，自动创建文件所在的目录并打开文件进行写入。
 * `log` 方法将日志数据写入文件，并保证每次写入都被刷新。
 */
class FileSink : public LogSink{
public:
    using ptr = std::shared_ptr<FileSink>;
    FileSink(const std::string &filename): _filename(filename) {
        util::file::create_directory(util::file::path(filename));   // 创建文件所在目录
        _ofs.open(_filename, std::ios::binary | std::ios::app);
        assert(_ofs.is_open());
    }
    // 获取日志文件名
    const std::string &file() { return _filename; }
    // 实现日志输出到文件
    void log(const char* data, size_t len) override {
        _ofs.write(data, len);
        _ofs.flush();   // 确保日志写入文件*
        if(_ofs.good() == false){
            std::cout << "日志输出文件失败！\n";
        }
    }
private:
    std::string _filename;  // 日志文件名
    std::ofstream _ofs;     // 输出文件流
};

/**
 * @brief RollSink 类实现了日志滚动，当日志文件达到最大大小时，会创建新文件。
 * 
 * `log` 方法在写入日志数据时，首先检查文件大小，如果超过最大限制，则创建一个新文件。
 */
class RollSink : public LogSink{
public:
    using ptr = std::shared_ptr<RollSink>;
    
    // 构造函数，接受日志文件基础名和单个文件的最大大小
    RollSink(const std::string &basename, size_t max_fsize)
        :_basename(basename), _max_fsize(max_fsize), _cur_fsize(0){
        util::file::create_directory(util::file::path(basename));
    }

    // 实现日志输出，支持文件滚动
    void log(const char* data, size_t len) override {
        initLogFile();          // 检查并初始化日志文件
        _ofs.write(data, len);
        _ofs.flush();           // 确保日志写入文件
        if(_ofs.good() == false){
            std::cout << "日志输出文件失败！\n";
        }
        _cur_fsize += len;      // 更新当前文件大小
    }
private:
    // 初始化日志文件，如果文件未打开或已达到最大大小，创建新文件
    void initLogFile(){
        if(_ofs.is_open() == false || _cur_fsize >= _max_fsize){
            _ofs.close();
            std::string name = createFilename();
            _ofs.open(name, std::ios::binary | std::ios::app);  
            assert(_ofs.is_open());
            _cur_fsize = 0;
        }
        return;
    }

    // 创建新的日志文件名，包含时间戳
    std::string createFilename(){
        time_t t = time(nullptr);
        struct tm lt;
        localtime_r(&t, &lt);
        
        std::stringstream filename;
        filename << _basename;            // 基础文件名
        filename << lt.tm_year + 1900;    // 时间信息
        filename << lt.tm_mon + 1;   
        filename << lt.tm_mday;
        filename << lt.tm_hour;
        filename << lt.tm_min;
        filename << lt.tm_sec;
        filename << "-";
        filename << _name_count++;
        filename << ".log";

        // char timebuf[64];
        // strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%S", &lt);    // 格式化时间
        // std::stringstream ss;
        // ss << _basename << timebuf << ".log";

        return  filename.str();
    }

private:
    size_t _name_count;
    std::string _basename;  // 日志文件基础名
    std::ofstream _ofs;     // 输出文件流
    size_t _max_fsize;      // 单个日志文件的最大大小
    size_t _cur_fsize;      // 当前日志文件的大小
};

/**
 * @brief SinkFactory 类用于创建不同类型的日志输出目标（Sink）。
 * 
 * 使用模板函数 `create` 来创建特定类型的日志输出对象，如 `StdoutSink`, `FileSink`, `RollSink` 等。
 */
class SinkFactory {
public:
    template<typename SinkType, typename ...Args>
    static LogSink::ptr create(Args &&...args){
        return std::make_shared<SinkType>(std::forward<Args>(args)...);
    }
};
}
#endif